home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / net / sun4.md / netIEXmit.c < prev    next >
C/C++ Source or Header  |  1991-03-16  |  18KB  |  614 lines

  1. /* 
  2.  * netIEXmit.c --
  3.  *
  4.  *    Routines to transmit packets on the Intel interface.
  5.  *
  6.  * Copyright 1985, 1988 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  *
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /sprite/src/kernel/net/sun3.md/RCS/netIEXmit.c,v 9.6 91/02/12 14:14:34 jhh Exp Locker: mgbaker $ SPRITE (Berkeley)";
  19. #endif
  20.  
  21. #include <sprite.h>
  22. #include <netIEInt.h>
  23. #include <sys.h>
  24. #include <list.h>
  25. #include <vmMach.h>
  26. #include <sync.h>
  27.  
  28. /*
  29.  * Extra bytes for short packets.
  30.  */
  31. char    *netIEXmitFiller;
  32.  
  33.  
  34.  
  35. /*
  36.  *----------------------------------------------------------------------
  37.  *
  38.  * OutputPacket --
  39.  *
  40.  *    Assemble and output the packet in the given scatter/gather element.
  41.  *    The ethernet header contains the address of the destination host
  42.  *    and the higher level protocol type already.
  43.  *
  44.  * Results:
  45.  *    None.
  46.  *
  47.  * Side effects:
  48.  *    Transmit command list is modified to contain the packet.
  49.  *
  50.  *----------------------------------------------------------------------
  51.  */
  52.  
  53. static void
  54. OutputPacket(etherHdrPtr, scatterGatherPtr, scatterGatherLength, statePtr)
  55.     Net_EtherHdr                        *etherHdrPtr;
  56.     register    Net_ScatterGather       *scatterGatherPtr;
  57.     int                    scatterGatherLength;
  58.     NetIEState                *statePtr;
  59. {
  60.     register    volatile NetIETransmitBufDesc    *xmitBufDescPtr;
  61.     register    volatile NetIETransmitCB       *xmitCBPtr;
  62.     register int            bufCount;
  63.     int                    totalLength;
  64.     register int            length;
  65.     register Address            bufAddr;
  66. #define VECTOR_LENGTH    20
  67.     int                    borrowedBytes[VECTOR_LENGTH];
  68.     int                    *borrowedBytesPtr;
  69.     char                *tmpBuffer;
  70.     int                    tmpBufSize;
  71. #if defined(sun3) || defined(sun4) 
  72.     Net_ScatterGather            newScatGathArr[NET_IE_NUM_XMIT_BUFFERS];
  73. #endif
  74.  
  75.     statePtr->transmitting = TRUE;
  76.     statePtr->curScatGathPtr = scatterGatherPtr;
  77. #if defined(sun3) || defined(sun4) 
  78.     /*
  79.      * Remap the packet into network addressible memory.
  80.      */
  81.     VmMach_NetMapPacket(scatterGatherPtr, scatterGatherLength, newScatGathArr);
  82.     scatterGatherPtr = newScatGathArr;
  83. #endif
  84.  
  85.     /*
  86.      * There is already a prelinked command list.  A pointer to the list
  87.      * and the array of buffer headers is gotten here.
  88.      */
  89.  
  90.     xmitCBPtr = statePtr->xmitCBPtr;
  91.     xmitBufDescPtr = statePtr->xmitBufAddr;
  92.  
  93.     totalLength = sizeof(Net_EtherHdr);
  94.  
  95.     /*
  96.      * If vector elements are two small we borrow bytes from the next
  97.      * element.  The borrowedBytes array is used to remember this.  We
  98.      * can't side-effect the main scatter-gather vector becuase that
  99.      * screws up retransmissions.
  100.      */
  101.     borrowedBytesPtr = borrowedBytes;
  102.     *borrowedBytesPtr = 0;
  103.     tmpBuffer = statePtr->netIEXmitTempBuffer;
  104.     tmpBufSize = XMIT_TEMP_BUFSIZE;
  105.     /*
  106.      * Put all of the pieces of the packet into the linked list of xmit
  107.      * buffers.
  108.      */
  109.     for (bufCount = 0 ; bufCount < scatterGatherLength ;
  110.      bufCount++, scatterGatherPtr++, borrowedBytesPtr++) {
  111.  
  112.     /*
  113.      * If is an empty buffer then skip it.  Length might even be negative
  114.      * if we have borrowed bytes from it to pad out to NET_IE_MIN_DMA_SIZE.
  115.      */
  116.     borrowedBytesPtr[1] = 0;
  117.     length = scatterGatherPtr->length - *borrowedBytesPtr;
  118.     if (length <= 0) {
  119.         continue;
  120.     }
  121.     bufAddr = scatterGatherPtr->bufAddr + *borrowedBytesPtr;
  122.     /*
  123.      * If the buffer is too small then it needs to be made bigger
  124.      * or the DMA hardware will overrun.  Also, check for buffers
  125.      * that start at odd addresses.  If one does, then it needs
  126.      * to be copied to another buffer with an even address.
  127.      * NB: There is only one temporary buffer.  Bad things will happen
  128.      * if more than one message uses this temporary buffer at once.
  129.      */
  130.     if ((length < NET_IE_MIN_DMA_SIZE) || ((int)bufAddr & 0x1)) {
  131.  
  132.         if (length > tmpBufSize) {
  133.         statePtr->transmitting = FALSE;
  134.         ENABLE_INTR();
  135.  
  136.         panic("IE OutputPacket: Odd addressed buffer too large.");
  137.         return;
  138.         }
  139.         bcopy(bufAddr, tmpBuffer, length);
  140.         if (length < NET_IE_MIN_DMA_SIZE) {
  141.         /*
  142.          * This element of the scatter/gather vector is too small;
  143.          * the controller DMA has to copy a minimum number of bytes.
  144.          * We take some bytes from the next non-zero sized element(s)
  145.          * to pad this one out.
  146.          */
  147.         register int numBorrowedBytes;
  148.         register int numAvailableBytes;
  149.         while (bufCount < scatterGatherLength - 1) {
  150.             numBorrowedBytes = NET_IE_MIN_DMA_SIZE - length;
  151.             numAvailableBytes = scatterGatherPtr[1].length -
  152.                     borrowedBytesPtr[1];
  153.             if (numBorrowedBytes > numAvailableBytes) {
  154.             numBorrowedBytes = numAvailableBytes;
  155.             }
  156.             if (numBorrowedBytes > 0) {
  157.             bcopy(scatterGatherPtr[1].bufAddr,
  158.                  &tmpBuffer[length], numBorrowedBytes);
  159.             borrowedBytesPtr[1] = numBorrowedBytes;
  160.             length += numBorrowedBytes;
  161.             }
  162.             if (length == NET_IE_MIN_DMA_SIZE) {
  163.             break;
  164.             } else {
  165.             bufCount++;
  166.             scatterGatherPtr++;
  167.             borrowedBytesPtr++;
  168.             borrowedBytesPtr[1] = 0;
  169.             }
  170.         }
  171.         length = NET_IE_MIN_DMA_SIZE;
  172.         }
  173.  
  174.         NET_IE_ADDR_FROM_SUN_ADDR(
  175.          (int) (tmpBuffer), (int) (xmitBufDescPtr->bufAddr));
  176.         /*
  177.          * Set up tmpBuffer for the next short segment.
  178.          */
  179.         tmpBuffer += length;
  180.         tmpBufSize -= length;
  181.         if ((int)tmpBuffer & 0x1) {
  182.         tmpBuffer++;
  183.         tmpBufSize--;
  184.         }
  185.     } else {
  186.         NET_IE_ADDR_FROM_SUN_ADDR(
  187.          (int) (bufAddr), (int) (xmitBufDescPtr->bufAddr));
  188.     }
  189.     NetBfShortSet(xmitBufDescPtr->bits, Eof, 0);
  190.     NetBfShortSet(xmitBufDescPtr->bits, CountLow, length & 0xFF);
  191.     NetBfShortSet(xmitBufDescPtr->bits, CountHigh, length >> 8);
  192.  
  193.     totalLength += length;
  194.     xmitBufDescPtr = (volatile NetIETransmitBufDesc *)
  195.         ((int) xmitBufDescPtr + NET_IE_CHUNK_SIZE);
  196.     }
  197.  
  198.     /*
  199.      * If the packet was too short, then hang some extra storage off of the
  200.      * end of it.
  201.      */
  202.  
  203.     if (totalLength < NET_ETHER_MIN_BYTES) {
  204.         NET_IE_ADDR_FROM_SUN_ADDR((int) netIEXmitFiller, 
  205.                         (int) xmitBufDescPtr->bufAddr); 
  206.     length = NET_ETHER_MIN_BYTES - totalLength;
  207.     if (length < MIN_XMIT_BUFFER_SIZE) {
  208.         length = MIN_XMIT_BUFFER_SIZE;
  209.     }
  210.     NetBfShortSet(xmitBufDescPtr->bits, CountLow, length & 0xFF);
  211.     NetBfShortSet(xmitBufDescPtr->bits, CountHigh, length >> 8);
  212.     } else {
  213.     xmitBufDescPtr = (volatile NetIETransmitBufDesc *)
  214.         ((int) xmitBufDescPtr - NET_IE_CHUNK_SIZE);
  215.     }
  216.     /*
  217.      * Finish off the packet.
  218.      */
  219.  
  220.     if (rpc_SanityCheck && (etherHdrPtr->type == NET_ETHER_SPRITE)) {
  221.     ReturnStatus     status;
  222.     status = Rpc_SanityCheck(scatterGatherLength, 
  223.             statePtr->curScatGathPtr, totalLength);
  224.     if (status != SUCCESS) {
  225.         panic("Sanity check failed on outgoing packet.\n");
  226.     }
  227.     }
  228.     NetBfShortSet(xmitBufDescPtr->bits, Eof, 1);
  229.     xmitCBPtr->destEtherAddr = etherHdrPtr->destination;
  230.     xmitCBPtr->type = etherHdrPtr->type;
  231.  
  232.     /*
  233.      * Append the command onto the command queue.
  234.      */
  235.  
  236.     *(short *) xmitCBPtr = 0;      /* Clear the status bits. */
  237.     NetBfWordSet(xmitCBPtr->bits, EndOfList, 1);
  238.     NetBfWordSet(xmitCBPtr->bits, Interrupt, 1);
  239.  
  240.  
  241.     /*
  242.      * Make sure that the last command was accepted and then
  243.      * start the command unit.
  244.      */
  245.  
  246.     NET_IE_CHECK_SCB_CMD_ACCEPT(statePtr->scbPtr);
  247.     NetBfShortSet(statePtr->scbPtr->cmdWord, CmdUnitCmd, NET_IE_CUC_START);
  248.     NET_IE_CHANNEL_ATTENTION(statePtr);
  249. }
  250.  
  251.  
  252. /*
  253.  *----------------------------------------------------------------------
  254.  *
  255.  * NetIEXmitInit --
  256.  *
  257.  *    Initialize the transmission queue structures.  This includes setting
  258.  *    up a template transmission command block and then if any packets are
  259.  *    ready starting to transmit.
  260.  *
  261.  * Results:
  262.  *    None.
  263.  *
  264.  * Side effects:
  265.  *    The transmission command block is initialized.
  266.  *
  267.  *----------------------------------------------------------------------
  268.  */
  269.  
  270. void
  271. NetIEXmitInit(statePtr)
  272.     NetIEState        *statePtr;
  273. {
  274.     register volatile NetIETransmitCB        *xmitCBPtr;
  275.     register volatile NetIETransmitBufDesc  *xmitBufDescPtr;
  276.     volatile NetIETransmitBufDesc        *newXmitBufDescPtr;
  277.     volatile NetXmitElement                *xmitElementPtr;
  278.     int         i;
  279.  
  280.     /*
  281.      * Initialize the transmit command header.
  282.      */
  283.  
  284.     xmitCBPtr = (NetIETransmitCB *) statePtr->cmdBlockPtr;
  285.     statePtr->xmitCBPtr = xmitCBPtr;
  286.     NetBfWordSet(xmitCBPtr->bits, CmdNumber, NET_IE_TRANSMIT);
  287.     NetBfWordSet(xmitCBPtr->bits, Suspend, 0);
  288.  
  289.     /*
  290.      * Now link in all of the buffer headers.
  291.      */
  292.  
  293.     xmitBufDescPtr = (volatile NetIETransmitBufDesc *) NIL;
  294.     for (i = 0; i < NET_IE_NUM_XMIT_BUFFERS; i++) {
  295.     newXmitBufDescPtr = (volatile NetIETransmitBufDesc *) 
  296.                 NetIEMemAlloc(statePtr);
  297.     if (newXmitBufDescPtr == (volatile NetIETransmitBufDesc *) NIL) {
  298.         panic( "Intel: No memory for the xmit buffers.\n");
  299.     }
  300.  
  301.     if (i == 0) {
  302.         statePtr->xmitBufAddr = newXmitBufDescPtr;
  303.         xmitCBPtr->bufDescOffset = 
  304.             NetIEOffsetFromSUNAddr((int) newXmitBufDescPtr,
  305.                 statePtr);
  306.     } else {
  307.         xmitBufDescPtr->nextTBD = 
  308.             NetIEOffsetFromSUNAddr((int) newXmitBufDescPtr,
  309.                 statePtr);
  310.     }
  311.  
  312.     xmitBufDescPtr = newXmitBufDescPtr;
  313.     }
  314.  
  315.     /*
  316.      * If there are packets on the queue then go ahead and send 
  317.      * the first one.
  318.      */
  319.  
  320.     if (!List_IsEmpty(statePtr->xmitList)) {
  321.     xmitElementPtr = (NetXmitElement *) List_First(statePtr->xmitList);
  322.     OutputPacket(xmitElementPtr->etherHdrPtr,
  323.              xmitElementPtr->scatterGatherPtr,
  324.              xmitElementPtr->scatterGatherLength, statePtr);
  325.     List_Move((List_Links *) xmitElementPtr, 
  326.           LIST_ATREAR(statePtr->xmitFreeList));
  327.     } else {
  328.     statePtr->transmitting = FALSE;
  329.     statePtr->curScatGathPtr = (Net_ScatterGather *) NIL;
  330.     }
  331.     return;
  332. }
  333.  
  334.  
  335. /*
  336.  *----------------------------------------------------------------------
  337.  *
  338.  * NetIEXmitDone --
  339.  *
  340.  *    This routine will process a completed transmit command.  It will
  341.  *    remove the command from the front of the transmit queue, 
  342.  *    and wakeup any waiting process.
  343.  *
  344.  * Results:
  345.  *    None.
  346.  *
  347.  * Side effects:
  348.  *    None.
  349.  *
  350.  *----------------------------------------------------------------------
  351.  */
  352.  
  353. void
  354. NetIEXmitDone(statePtr)
  355.     NetIEState    *statePtr;
  356. {
  357.     register    volatile NetXmitElement     *xmitElementPtr;
  358.     register    volatile NetIETransmitCB    *cmdPtr;
  359.     Net_ScatterGather    *curScatGathPtr;
  360.  
  361.     /*
  362.      * If there is nothing that is currently being sent then something is
  363.      * wrong.
  364.      */
  365.     if (statePtr->curScatGathPtr == (Net_ScatterGather *) NIL) {
  366. #ifndef sun4
  367.     /*
  368.      * Need to fix this for the sun4.
  369.      */
  370.     printf( "NetIEXmitDone: No current packet\n.");
  371. #endif
  372.     return;
  373.     }
  374.     curScatGathPtr = statePtr->curScatGathPtr;
  375.  
  376.     statePtr->stats.packetsSent++;
  377.  
  378.     /*
  379.      * Mark the packet as done.
  380.      */
  381.     curScatGathPtr->done = TRUE;
  382.     if (curScatGathPtr->mutexPtr != (Sync_Semaphore *) NIL) {
  383.     NetOutputWakeup(curScatGathPtr->mutexPtr);
  384.     }
  385.  
  386.     /*
  387.      * Record statistics about the packet.
  388.      */
  389.     cmdPtr = statePtr->xmitCBPtr;
  390.     if (NetBfWordTest(cmdPtr->bits, TooManyCollisions, 1)) {
  391.     statePtr->stats.xmitCollisionDrop++;
  392.     statePtr->stats.collisions += 16;
  393.     } else {
  394.     statePtr->stats.collisions += NetBfWordGet(cmdPtr->bits, NumCollisions);
  395.     }
  396.     if (NetBfWordTest(cmdPtr->bits, CmdOK, 0)) {
  397.     statePtr->stats.xmitPacketsDropped++;
  398.     }
  399.  
  400.     /*
  401.      * If there are more packets to send then send the first one on
  402.      * the queue.  Otherwise there is nothing being transmitted.
  403.      */
  404.     if (!List_IsEmpty(statePtr->xmitList)) {
  405.     xmitElementPtr = (NetXmitElement *) List_First(statePtr->xmitList);
  406.     OutputPacket(xmitElementPtr->etherHdrPtr,
  407.              xmitElementPtr->scatterGatherPtr,
  408.              xmitElementPtr->scatterGatherLength, statePtr);
  409.     List_Move((List_Links *) xmitElementPtr, 
  410.           LIST_ATREAR(statePtr->xmitFreeList));
  411.     } else {
  412.     statePtr->transmitting = FALSE;
  413.     statePtr->curScatGathPtr = (Net_ScatterGather *) NIL;
  414.     }
  415.     return;
  416. }
  417.  
  418.  
  419. /*
  420.  *----------------------------------------------------------------------
  421.  *
  422.  * NetIEOutput --
  423.  *
  424.  *    Output a packet.  The procedure is to either put the packet onto the 
  425.  *    queue of outgoing packets if packets are already being sent, or 
  426.  *    otherwise to send the packet directly.  The elements of the scatter 
  427.  *    array which come into this routine must satisfy the following two 
  428.  *    properties:
  429.  *
  430.  *    1) No buffer must be below the size MIN_XMIT_BUFFER_SIZE.  If one is
  431.  *       then a warning will be printed and there is a good chance that
  432.  *       the packet will not make it.
  433.  *    2) No buffer can start on an odd boundary.  It appears that the Intel
  434.  *       chip drops the low order bit of the address.  Thus if a buffer
  435.  *       begins on an odd boundary the actual buffer sent will have one
  436.  *       extra byte at the front.  If a buffer does begin on an odd 
  437.  *       boundary then a warning message is printed and the packet is
  438.  *       sent anyway.
  439.  *
  440.  *    In theory the statusPtr argument should be filled in by NetIEXmitDone
  441.  *    when the command completes.  We fill it in here because a 
  442.  *    mechansism doesn't exist for getting the pointer to NetIEXmitDone,
  443.  *    and because it doesn't appear that NetIEXmitDone would ever
  444.  *    return anything other than SUCCESS.
  445.  *
  446.  * Results:
  447.  *    None.
  448.  *
  449.  * Side effects:
  450.  *    Queue of packets modified.
  451.  *
  452.  *----------------------------------------------------------------------
  453.  */
  454.  
  455. ReturnStatus
  456. NetIEOutput(interPtr, hdrPtr, scatterGatherPtr, scatterGatherLength, rpc,
  457.         statusPtr)
  458.     Net_Interface            *interPtr;
  459.     Address                hdrPtr;
  460.     register    Net_ScatterGather    *scatterGatherPtr;
  461.     int                    scatterGatherLength;
  462.     Boolean                rpc;        /* Is this an rpc? */
  463.     ReturnStatus            *statusPtr;  /* Return status. */
  464. {
  465.     register volatile NetXmitElement    *xmitPtr;
  466.     NetIEState                *statePtr;
  467.     int                    i;
  468.     Net_ScatterGather            *gathPtr;
  469.     Net_EtherHdr            *etherHdrPtr = (Net_EtherHdr *) hdrPtr;
  470.  
  471.     statePtr = (NetIEState *) interPtr->interfaceData;
  472.     DISABLE_INTR();
  473.  
  474.     statePtr->stats.packetsOutput++;
  475.  
  476.     /*
  477.      * Verify that the scatter gather array is not too large.  There is a fixed
  478.      * upper bound because the list of transmit buffers is preallocated.
  479.      */
  480.  
  481.     if (scatterGatherLength >= NET_IE_NUM_XMIT_BUFFERS) {
  482.     scatterGatherPtr->done = TRUE;
  483.  
  484.     printf("Intel: Packet in too many pieces\n");
  485.     ENABLE_INTR();
  486.     return FAILURE;
  487.     }
  488.     statePtr->stats.bytesSent += sizeof(Net_EtherHdr);
  489.     for (i = scatterGatherLength, gathPtr = scatterGatherPtr; 
  490.      i > 0; 
  491.      i--, gathPtr++) { 
  492.     statePtr->stats.bytesSent += gathPtr->length; 
  493.     } 
  494.  
  495.     /*
  496.      * See if the packet is for us.  In this case just copy in the packet
  497.      * and call the higher level routine.
  498.      */
  499.  
  500.     if (NET_ETHER_COMPARE(statePtr->etherAddress, etherHdrPtr->destination)) {
  501.     int i, length;
  502.  
  503.         length = sizeof(Net_EtherHdr);
  504.         for (i = 0; i < scatterGatherLength; i++) {
  505.             length += scatterGatherPtr[i].length;
  506.         }
  507.  
  508.         if (length <= NET_ETHER_MAX_BYTES) {
  509.         register Address bufPtr;
  510.  
  511.         etherHdrPtr->source = statePtr->etherAddress;
  512.  
  513.         bufPtr = (Address)statePtr->loopBackBuffer;
  514.         bcopy((Address)etherHdrPtr, bufPtr, sizeof(Net_EtherHdr));
  515.         bufPtr += sizeof(Net_EtherHdr);
  516.             Net_GatherCopy(scatterGatherPtr, scatterGatherLength, bufPtr);
  517.  
  518.         Net_Input(interPtr, (Address)statePtr->loopBackBuffer, 
  519.             length);
  520.         }
  521.  
  522.         scatterGatherPtr->done = TRUE;
  523.     if (statusPtr != (ReturnStatus *) NIL) {
  524.         *statusPtr = SUCCESS;
  525.     }
  526.     ENABLE_INTR();
  527.     return SUCCESS;
  528.     }
  529.  
  530.     /*
  531.      * If no packet is being sent then go ahead and send this one.
  532.      */
  533.  
  534.     if (!statePtr->transmitting) {
  535.     OutputPacket(etherHdrPtr, scatterGatherPtr, scatterGatherLength,
  536.         statePtr);
  537.     if (statusPtr != (ReturnStatus *) NIL) {
  538.         *statusPtr = SUCCESS;
  539.     }
  540.     ENABLE_INTR();
  541.     return SUCCESS;
  542.     }
  543.  
  544.     /*
  545.      * There is a packet being sent so this packet has to be put onto the
  546.      * transmission queue.  Get an element off of the transmission free list.  
  547.      * If none available then drop the packet.
  548.      */
  549.  
  550.     if (List_IsEmpty(statePtr->xmitFreeList)) {
  551.         scatterGatherPtr->done = TRUE;
  552.     ENABLE_INTR();
  553.     return FAILURE;
  554.     }
  555.  
  556.     xmitPtr = (volatile NetXmitElement *)
  557.     List_First((List_Links *) statePtr->xmitFreeList);
  558.  
  559.     List_Remove((List_Links *) xmitPtr);
  560.  
  561.     /*
  562.      * Initialize the list element.
  563.      */
  564.  
  565.     xmitPtr->etherHdrPtr = etherHdrPtr;
  566.     xmitPtr->scatterGatherPtr = scatterGatherPtr;
  567.     xmitPtr->scatterGatherLength = scatterGatherLength;
  568.  
  569.     /* 
  570.      * Put onto the transmission queue.
  571.      */
  572.  
  573.     List_Insert((List_Links *) xmitPtr, LIST_ATREAR(statePtr->xmitList)); 
  574.  
  575.     if (statusPtr != (ReturnStatus *) NIL) {
  576.     *statusPtr = SUCCESS;
  577.     }
  578.     ENABLE_INTR();
  579.     return SUCCESS;
  580. }
  581.  
  582. /*
  583.  *----------------------------------------------------------------------
  584.  *
  585.  * NetIEXmitDrop --
  586.  *
  587.  *    This drops the current output packet by marking its scatter/gather
  588.  *    vector as DONE and notifying the process waiting for its
  589.  *    output to complete.  This is called in the beginning of the
  590.  *    Restart sequence.
  591.  *
  592.  * Results:
  593.  *    None.
  594.  *
  595.  * Side effects:
  596.  *    Resets curScatGathPtr and notifies any process waiting on output.
  597.  *
  598.  *----------------------------------------------------------------------
  599.  */
  600.  
  601. void
  602. NetIEXmitDrop(statePtr)
  603.     NetIEState        *statePtr;
  604. {
  605.     if (statePtr->curScatGathPtr != (Net_ScatterGather *) NIL) {
  606.     statePtr->curScatGathPtr->done = TRUE;
  607.     if (statePtr->curScatGathPtr->mutexPtr != (Sync_Semaphore *) NIL) {
  608.         NetOutputWakeup(statePtr->curScatGathPtr->mutexPtr);
  609.     }
  610.     statePtr->curScatGathPtr = (Net_ScatterGather *) NIL;
  611.     }
  612.     return;
  613. }
  614.